using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace TXLooker.WorkItemExtention.UI.QueryManager
{
    internal class QueryParseManager
    {
        // Fields
        private Dictionary<string, FieldDefinition> m_fieldDictionary;
        private List<Group> m_groupCollection;
        private List<QueryParameters> m_queriesList;
        private WorkItemStore m_store;
        private string m_WIQL;
        public const string Operators = "(=|<>|<=|<|>|>=|IN|NOT UNDER|UNDER|EVER|CONTAINS|NOT CONTAINS)";

        // Methods
        protected QueryParseManager()
        {
            this.m_fieldDictionary = new Dictionary<string, FieldDefinition>();
            this.m_queriesList = new List<QueryParameters>();
            this.m_groupCollection = new List<Group>();
        }

        public QueryParseManager(string wiqlString, WorkItemStore store)
            : this()
        {
            this.m_queriesList.Capacity = 100;
            this.m_WIQL = wiqlString;
            this.m_store = store;
            this.OnLoad();
        }

        private void FindGroupClauses(string wiql)
        {
            StringBuilder builder = new StringBuilder(wiql);
            builder.Replace("(", string.Empty);
            builder.Replace(")", string.Empty);
            List<string> paramsList = this.GetParamsList(builder.ToString());
            this.ParseParams(paramsList, ref this.m_queriesList);
            char[] chArray = wiql.ToCharArray();
            int num = 0;
            int num2 = 0;
            int index = 0;
            while (index < chArray.Length)
            {
                if (chArray[index] == '(')
                {
                    num++;
                }
                else if (chArray[index] == ')')
                {
                    num2++;
                }
                index++;
            }
            int fistRow = 0;
            int lastRow = 0;
            int num6 = 0;
            string input = wiql;
            for (uint i = 0; i < num; i++)
            {
                MatchCollection matchs = new Regex(@"\((?=[^\(]*\)).*?\)").Matches(input);
                foreach (Match match in matchs)
                {
                    input = input.Replace(match.Value, string.Empty);
                    int num8 = -1;
                    int num9 = -1;
                    List<string> list2 = this.GetParamsList(match.Value);
                    for (index = 0; index < paramsList.Count; index++)
                    {
                        if (list2.Count > 0)
                        {
                            if (paramsList[index].Contains(list2[0]))
                            {
                                num8 = index;
                            }
                            if (paramsList[index].Contains(list2[list2.Count - 1]))
                            {
                                num9 = index;
                            }
                        }
                    }
                    if (num8 == num9)
                    {
                        if ((num8 < fistRow) && (num9 != lastRow))
                        {
                            if ((num8 == -1) && (num9 == -1))
                            {
                                num8 = lastRow - fistRow;
                                if (num8 > 0)
                                {
                                    this.GroupCollection.Add(new Group(num8, lastRow, num6 += 3));
                                }
                            }
                            else
                            {
                                this.GroupCollection.Add(new Group(num8, lastRow, num6 += 3));
                            }
                        }
                        else
                        {
                            this.GroupCollection.Add(new Group(fistRow, num9, num6 += 3));
                        }
                    }
                    else if ((num8 >= 0) && (num9 > 0))
                    {
                        if ((num8 < fistRow) && (num9 > lastRow))
                        {
                            this.GroupCollection.Add(new Group(num8, num9, num6 += 3));
                            fistRow = num8;
                            lastRow = num9;
                        }
                        else
                        {
                            this.GroupCollection.Add(new Group(num8, num9, 0));
                            fistRow = num8;
                            lastRow = num9;
                        }
                    }
                }
            }
        }

        private static string GetAndOr(string str)
        {
            if (str.Contains("AND"))
            {
                return "And";
            }
            if (!str.Contains("OR"))
            {
                throw new Exception();
            }
            return "Or";
        }

        private string GetField(string str)
        {
            StringBuilder builder = new StringBuilder(str);
            builder.Replace("[", string.Empty);
            builder.Replace("]", string.Empty);
            builder.Replace(" ", string.Empty);
            FieldDefinition definition = this.m_fieldDictionary[builder.ToString()];
            return definition.Name;
        }

        private string GetOperator(string oper)
        {
            switch (oper)
            {
                case "NOT CONTAINS":
                    return "Does Not Contain";

                case "IN":
                    return "In";

                case "EVER":
                    return "Was Ever";

                case "UNDER":
                    return "Under";

                case "NOT UNDER":
                    return "Not Under";

                case "CONTAINS":
                    return "Contains";
            }
            return oper;
        }

        private List<string> GetParamsList(string str)
        {
            List<string> list = new List<string>();
            MatchCollection matchs = new Regex(string.Format(@"\s*(OR\s*|AND\s*|)\[.+?\]\s*{0}\s*(\(.*?\)|\'.*?\'|(@project|@me|@Project|@Me)|(@Today.*?|@today.*?))", "(=|<>|<=|<|>|>=|IN|NOT UNDER|UNDER|EVER|CONTAINS|NOT CONTAINS)")).Matches(str);
            foreach (Match match in matchs)
            {
                list.Add(match.Value);
            }
            return list;
        }

        private string GetValue(string str, bool bIN)
        {
            StringBuilder builder = new StringBuilder(str);
            builder.Replace("'", string.Empty);
            if (bIN)
            {
                char ch = '(';
                builder.Replace(ch.ToString(), string.Empty);
                ch = ')';
                builder.Replace(ch.ToString(), string.Empty);
            }
            else
            {
                builder.Replace('\''.ToString(), string.Empty);
            }
            return builder.ToString();
        }

        private void OnLoad()
        {
            foreach (FieldDefinition definition in this.m_store.FieldDefinitions)
            {
                this.m_fieldDictionary.Add(definition.ReferenceName, definition);
            }
            this.RemoveWithWhere();
            this.RemoveWithOrderBy();
            this.FindGroupClauses(this.m_WIQL);
        }

        private void ParseParams(List<string> paramsList, ref List<QueryParameters> m_queriesList)
        {
            foreach (string str in paramsList)
            {
                Regex regex = new Regex(@"\s*(AND|OR)");
                string input = str;
                string oldValue = string.Empty;
                string field = string.Empty;
                string oper = string.Empty;
                string str6 = string.Empty;
                if (regex.Matches(str).Count == 1)
                {
                    oldValue = regex.Match(str).Value.Replace(" ", string.Empty);
                    input = str.Replace(oldValue, string.Empty);
                    oldValue = GetAndOr(oldValue);
                }
                regex = new Regex(@"\[[\w.]+\]");
                if (regex.Matches(input).Count != 1)
                {
                    throw new Exception("");
                }
                field = this.GetField(regex.Match(input).Value);
                regex = new Regex(string.Format(@"\s*{0}", "(=|<>|<=|<|>|>=|IN|NOT UNDER|UNDER|EVER|CONTAINS|NOT CONTAINS)"));
                if (regex.Matches(str).Count != 1)
                {
                    throw new Exception("");
                }
                oper = regex.Match(str).Value.Trim(new char[] { ' ' });
                oper = this.GetOperator(oper);
                regex = new Regex(@"\s*(\'.*?\'|@project|@me|@.*|[0-9]|\(.*?\))");
                MatchCollection matchs = regex.Matches(str);
                if (regex.Matches(input).Count != 1)
                {
                    throw new Exception("");
                }
                if ((oper == "IN") || (oper == "In"))
                {
                    str6 = this.GetValue(regex.Match(str).Value, true).Replace(" ", string.Empty);
                }
                else
                {
                    str6 = this.GetValue(regex.Match(str).Value, false);
                }
                str6 = str6.Trim(new char[] { ' ' });
                m_queriesList.Add(new QueryParameters(oldValue, field, oper, str6));
            }
        }

        private void RemoveWithOrderBy()
        {
            this.m_WIQL = StringFormating.RemoveFromQueryAfter(this.m_WIQL, "ORDER");
        }

        private void RemoveWithWhere()
        {
            this.m_WIQL = StringFormating.RemoveFromQueryBefore(this.m_WIQL, true, new string[] { "WHERE" });
        }

        // Properties
        public List<Group> GroupCollection
        {
            get
            {
                return this.m_groupCollection;
            }
            protected set
            {
                if (object.ReferenceEquals(value, null))
                {
                    throw new NullReferenceException();
                }
                this.m_groupCollection = value;
            }
        }

        public List<QueryParameters> QueryList
        {
            get
            {
                if (this.m_queriesList.Count <= 0)
                {
                    throw new Exception();
                }
                return this.m_queriesList;
            }
        }
    }


}